//------------------------------------------------------------------------------
// XMLDocument.cpp
//------------------------------------------------------------------------------
// Copyright 1995-2006 Microsoft Corporation.  All Rights Reserved.
//
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF 
// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO 
// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A 
// PARTICULAR PURPOSE.
//------------------------------------------------------------------------------
//
// Helper classes for the XML DOM
//
//------------------------------------------------------------------------------

#include "StdAfx.h"
#include "XMLDocument.h"
#include "MemUtils.h"
#include "Utils.h"

////////////////////////////////////////////////////////////////////////////////

HRESULT CXMLNode::SelectNode(PCWSTR pszPattern, __deref_out IXMLDOMNode **ppxmlNode) const
{
    HRESULT hr = E_FAIL;
    if (_spxmlNode)
    {
        hr = E_INVALIDARG;
        if (pszPattern)
        {
            // Navigate to the requested node and reset the current node
            CComPtr<IXMLDOMNode> spxmlNode;
            hr = _spxmlNode->selectSingleNode((BSTR)pszPattern, &spxmlNode);
            if (hr == S_FALSE)
            {
                hr = STG_E_FILENOTFOUND;
            }

            if (SUCCEEDED(hr))
            {
                // Set the node pointer to the newly found node
                *ppxmlNode = spxmlNode.Detach();
            }
        }
    }
    return hr;
}

HRESULT CXMLNode::TestPattern(PCWSTR pszPattern) const
{
    HRESULT hr = E_FAIL;
    if (_spxmlNode)
    {
        // Find the node
        CComPtr<IXMLDOMNode> spxmlNode;
        hr = S_OK;
        if (!pszPattern)
        {
            // Use the current node
            spxmlNode = _spxmlNode;
        }
        else 
        {
            hr = _spxmlNode->selectSingleNode((BSTR)pszPattern, &spxmlNode);
        }
    }
    return hr;
}

HRESULT CXMLNode::EnumerateNodes(PCWSTR pszPattern, __deref_out IXMLDOMNodeList **ppxmlNodeList) const
{
    HRESULT hr = E_FAIL;
    if (_spxmlNode)
    {
        hr = E_INVALIDARG;
        if (pszPattern)
        {
            // Create the list of nodes
            CComPtr<IXMLDOMNodeList> spxmlNodeList;
            hr = _spxmlNode->selectNodes((BSTR)pszPattern, &spxmlNodeList);

            if (hr == S_FALSE)
            {
                hr = STG_E_FILENOTFOUND;
            }

            if (SUCCEEDED(hr))
            {
                // Set the list pointer to the newly created list
                *ppxmlNodeList = spxmlNodeList.Detach();
            }
        }
    }
    return hr;
}

HRESULT CXMLNode::AddNode(PCWSTR pszPattern, PCWSTR pszNodeName, PCWSTR pszNodeText)
{
    HRESULT hr = E_FAIL;
    if (_spxmlNode)
    {
        // Create a temporary node
        CComPtr<IXMLDOMNode> spxmlNode;
        if (!pszPattern)
        {
            spxmlNode = _spxmlNode;
            hr = S_OK;
        }
        else
        {
            hr = _spxmlNode->selectSingleNode((BSTR)pszPattern, &spxmlNode);
        }

        if (SUCCEEDED(hr))
        {
            CComPtr<IXMLDOMDocument> spxmlDoc;
            hr = _spxmlNode->get_ownerDocument(&spxmlDoc);
            if (SUCCEEDED(hr))
            {
                // Create the new node
                CComPtr<IXMLDOMElement> spxmlElt;
                hr = spxmlDoc->createElement((BSTR)pszNodeName, &spxmlElt);
                if (SUCCEEDED(hr))
                {
                    // Set the value of the node
                    CComPtr<IXMLDOMNode> spxmlNodeNew;
                    hr = spxmlNode->appendChild(spxmlElt, &spxmlNodeNew);
                    if (SUCCEEDED(hr) && pszNodeText)
                    {
                        hr = spxmlNodeNew->put_text((BSTR)pszNodeText);
                    }
                }
            }
        }
    }
    return hr;
}

HRESULT CXMLNode::RemoveNode(PCWSTR pszPattern, PCWSTR pszNodeName)
{
    HRESULT hr = E_FAIL;
    if (_spxmlNode)
    {
        // Create a temporary node
        CComPtr<IXMLDOMNode> spxmlNode;
        if (!pszPattern)
        {
            spxmlNode = _spxmlNode;
            hr = S_OK;
        }
        else
        {
            hr = _spxmlNode->selectSingleNode((BSTR)pszPattern, &spxmlNode);
        }

        if (SUCCEEDED(hr))
        {
            // Find the node to remove
            CComPtr<IXMLDOMNode> spxmlNodeChild;
            hr = spxmlNode->selectSingleNode((BSTR)pszNodeName, &spxmlNodeChild);
            if (SUCCEEDED(hr))
            {
                // Remove it
                spxmlNode->removeChild(spxmlNodeChild, NULL);
            }
        }

        if (hr == S_FALSE)
        {
            hr = STG_E_FILENOTFOUND;
        }
    }
    return hr;
}

HRESULT CXMLNode::GetNodeValue(PCWSTR pszPattern, __inout CComVariant &svarVal) const
{
    HRESULT hr = E_FAIL;
    if (_spxmlNode)
    {
        // Find the node
        CComPtr<IXMLDOMNode> spxmlNode;
        hr = S_OK;
        if (!pszPattern)
        {
            // Use the current node
            spxmlNode = _spxmlNode;
        } 
        else 
        {
            hr = _spxmlNode->selectSingleNode((BSTR)pszPattern, &spxmlNode);
        }

        // Get the value
        if (SUCCEEDED(hr))
        {
            hr = spxmlNode->get_nodeTypedValue(&svarVal);
        }

        if (hr == S_FALSE)
        {
            hr = STG_E_FILENOTFOUND;
        }
    }
    return hr;
}

HRESULT CXMLNode::GetNodeValue(PCWSTR pszPattern, __out long *plVal) const
{
    HRESULT hr = E_INVALIDARG;
    if (plVal)
    {
        *plVal = 0;
        // Call the generic version and convert
        CComVariant svarValue;
        hr = GetNodeValue(pszPattern, svarValue);
        if (SUCCEEDED(hr))
        {
            hr = svarValue.ChangeType(VT_I4);
            if (S_OK == hr)
            {
                *plVal = svarValue.lVal;
            }
        }
    }
    return hr;
}

HRESULT CXMLNode::GetNodeValue(PCWSTR pszPattern, __out DWORD *pdwVal) const
{
    HRESULT hr = E_INVALIDARG;
    if (pdwVal)
    {
        *pdwVal = 0;
        // Call the generic version and convert
        CComVariant svarValue;
        hr = GetNodeValue(pszPattern, svarValue);
        if (SUCCEEDED(hr))
        {
            hr = svarValue.ChangeType(VT_UI4);
            if (SUCCEEDED(hr))
            {
                *pdwVal = svarValue.lVal;
            }
        }
    }
    return hr;
}

HRESULT CXMLNode::GetNodeValue(PCWSTR pszPattern, __out_ecount(cch) PWSTR pszVal, DWORD cch) const
{
    HRESULT hr = E_INVALIDARG;
    if (pszPattern && pszVal && cch)
    {
        *pszVal = 0;

        // Call the generic version and copy
        CComVariant svarValue;
        hr = GetNodeValue(pszPattern, svarValue);
        if (SUCCEEDED(hr))
        {
            hr = StringCchCopy(pszVal, cch, svarValue.bstrVal);
        }
    }
    return hr;
}

HRESULT CXMLNode::GetNodeValue(PCWSTR pszPattern, __out UINT *puiVal) const
{
    HRESULT hr = E_INVALIDARG;
    if (puiVal)
    {
        *puiVal = 0;
        // Call the generic version and convert
        CComVariant svarValue;
        hr = GetNodeValue(pszPattern, svarValue);
        if (SUCCEEDED(hr))
        {
            hr = svarValue.ChangeType(VT_UINT);
            if (SUCCEEDED(hr)) 
            {
                *puiVal = svarValue.uintVal;
            }
        }
    }
    return hr;
}

HRESULT CXMLNode::GetNodeValue(PCWSTR pszPattern, __out float *pflVal) const
{
    HRESULT hr = E_INVALIDARG;
    if (pflVal)
    {
        *pflVal = 0;
        // Call the generic version and convert
        CComVariant svarValue;
        hr = GetNodeValue(pszPattern, svarValue);
        if (SUCCEEDED(hr))
        {
            hr = svarValue.ChangeType(VT_R4);
            if (SUCCEEDED(hr)) 
            {
                *pflVal = svarValue.fltVal;
            }
        }
    }
    return hr;
}

HRESULT CXMLNode::GetNodeValue(PCWSTR pszPattern, __out double *pdblVal) const
{
    HRESULT hr = E_INVALIDARG;
    if (pdblVal)
    {
        *pdblVal = 0;
        // Call the generic version and convert
        CComVariant svarValue;
        hr = GetNodeValue(pszPattern, svarValue);
        if (SUCCEEDED(hr))
        {
            hr = svarValue.ChangeType(VT_R8);
            if (SUCCEEDED(hr)) 
            {
                *pdblVal = svarValue.dblVal;
            }
        }
    }
    return hr;
}

HRESULT CXMLNode::SetNodeText(PCWSTR pszPattern, PCWSTR pszText)
{
    HRESULT hr = E_FAIL;
    if (_spxmlNode)
    {
        // Find the node
        CComPtr<IXMLDOMNode> spxmlNode;
        hr = S_OK;
        if (!pszPattern)
        {
            // Use the current node
            spxmlNode = _spxmlNode;
        }
        else 
        {
            hr = _spxmlNode->selectSingleNode((BSTR)pszPattern, &spxmlNode);
        }

        // Set the text
        if (SUCCEEDED(hr))
        {
            hr = spxmlNode->put_text((BSTR)pszText);
        }

        if (hr == S_FALSE)
        {
            hr = STG_E_FILENOTFOUND;
        }
    }
    return hr;
}

HRESULT CXMLNode::GetAttribute(PCWSTR pszPattern, PCWSTR pszName, __inout CComVariant &svarVal) const
{
    HRESULT hr = E_FAIL;
    if (_spxmlNode)
    {
        // Create a temporary node
        CComPtr<IXMLDOMNode> spxmlNode;
        if (!pszPattern)
        {
            spxmlNode = _spxmlNode;
            hr = S_OK;
        }
        else 
        {
            hr = _spxmlNode->selectSingleNode((BSTR)pszPattern, &spxmlNode);
        }

        if (SUCCEEDED(hr))
        {
            CComPtr<IXMLDOMNamedNodeMap> spxmlMapAttrs;
            hr = spxmlNode->get_attributes(&spxmlMapAttrs);
            if (SUCCEEDED(hr))
            {
                CComPtr<IXMLDOMNode> spxmlNodeAttr;
                hr = spxmlMapAttrs->getNamedItem((BSTR)pszName, &spxmlNodeAttr);
                if (SUCCEEDED(hr)) 
                {
                    hr = spxmlNodeAttr->get_nodeValue(&svarVal);
                }
            }
        }
    }
    return hr;
}

HRESULT CXMLNode::_GetTypedAttribute(PCWSTR pszPattern, PCWSTR pszName, VARTYPE vt, __inout CComVariant &svarValue) const
{
    HRESULT hr = GetAttribute(pszPattern, pszName, svarValue);
    if (SUCCEEDED(hr))
    {
        hr = svarValue.ChangeType(vt);
    }
    return hr;
}

HRESULT CXMLNode::GetAttribute(PCWSTR pszPattern, PCWSTR pszName, __out float *pflVal) const
{
    HRESULT hr = E_INVALIDARG;
    if (pszName && pflVal) 
    {
        *pflVal = 0;

        // Call the generic version and convert
        CComVariant svarValue;
        hr = _GetTypedAttribute(pszPattern, pszName, VT_R4, svarValue);
        if (SUCCEEDED(hr))
        {
            *pflVal = svarValue.fltVal;
        }
    }
    return hr;
}

HRESULT CXMLNode::GetAttribute(PCWSTR pszName, __inout CComVariant &svarVal) const
{
    HRESULT hr = E_FAIL;
    if (_spxmlNode)
    {
        hr = E_INVALIDARG;
        if (pszName)
        {
            // Get the collection of attributes for the current node
            CComPtr<IXMLDOMNamedNodeMap> spxmlMapAttrs;
            hr = _spxmlNode->get_attributes(&spxmlMapAttrs);
            if (hr == S_OK)
            {
                // Get the node for the attribute we want
                CComPtr<IXMLDOMNode> spxmlNodeAttr;
                hr = spxmlMapAttrs->getNamedItem((BSTR)pszName, &spxmlNodeAttr);
                if (hr == S_OK)
                {
                    // Get the value
                    hr = spxmlNodeAttr->get_nodeValue(&svarVal);
                }
            }

            if (hr == S_FALSE)
            {
                hr = STG_E_FILENOTFOUND;
            }
        }
    }
    return hr;
}

HRESULT CXMLNode::GetAttribute(PCWSTR pszName, __out_ecount(cch) PWSTR pszVal, DWORD cch) const
{
    HRESULT hr = E_INVALIDARG;
    if (pszName && pszVal && cch)
    {
        *pszVal = 0;

        // Call the generic version and copy
        CComVariant svarValue;
        hr = GetAttribute(pszName, svarValue);
        if (SUCCEEDED(hr))
        {
            hr = StringCchCopy(pszVal, cch, svarValue.bstrVal);
        }
    }
    return hr;
}

HRESULT CXMLNode::_GetTypedAttribute(PCWSTR pszName, VARTYPE vt, __inout CComVariant &svarValue) const
{
    HRESULT hr = GetAttribute(pszName, svarValue);
    if (SUCCEEDED(hr))
    {
        hr = svarValue.ChangeType(VT_INT);
    }
    return hr;
}

HRESULT CXMLNode::GetAttribute(PCWSTR pszName, __out int *piVal) const
{
    HRESULT hr E_INVALIDARG;
    if (pszName && piVal)
    {
        *piVal = 0;

        // Call the generic version and convert
        CComVariant svarValue;
        hr = _GetTypedAttribute(pszName, VT_INT, svarValue);
        if (SUCCEEDED(hr))
        {
            *piVal = svarValue.intVal;
        }
    }
    return hr;
}

HRESULT CXMLNode::GetAttribute(PCWSTR pszName, __out long *plVal) const
{
    HRESULT hr = E_INVALIDARG;
    if (pszName && plVal)
    {
        *plVal = 0;

        // Call the generic version and convert
        CComVariant svarValue;
        hr = _GetTypedAttribute(pszName, VT_I4, svarValue);
        if (SUCCEEDED(hr))
        {
            *plVal = svarValue.lVal;
        }
    }
    return hr;
}

HRESULT CXMLNode::GetAttribute(PCWSTR pszName, __out UINT *puiVal) const
{
    HRESULT hr = E_INVALIDARG;
    if (pszName && puiVal)
    {
        *puiVal = 0;

        // Call the generic version and convert
        CComVariant svarValue;
        hr = _GetTypedAttribute(pszName, VT_UINT, svarValue);
        if (SUCCEEDED(hr))
        {
            *puiVal = svarValue.uintVal;
        }
    }
    return hr;
}

HRESULT CXMLNode::GetAttribute(PCWSTR pszName, __out DWORD *pdwVal) const
{
    HRESULT hr = E_INVALIDARG;
    if (pszName && pdwVal) 
    {
        *pdwVal = 0;

        // Call the generic version and convert
        CComVariant svarValue;
        hr = _GetTypedAttribute(pszName, VT_UI4, svarValue);
        if (SUCCEEDED(hr))
        {
            *pdwVal = svarValue.ulVal;
        }
    }
    return hr;
}

HRESULT CXMLNode::SetAttribute(PCWSTR pszName, __in CComVariant &svarVal)
{
    HRESULT hr = E_FAIL;
    if (_spxmlNode)
    {
        hr = E_INVALIDARG;
        if (pszName)
        {
            // Get the collection of attributes for the current node
            CComPtr<IXMLDOMNamedNodeMap> spxmlMapAttrs;
            hr = _spxmlNode->get_attributes(&spxmlMapAttrs);
            if (SUCCEEDED(hr))
            {
                CComPtr<IXMLDOMDocument> spxmlDoc;
                hr = _spxmlNode->get_ownerDocument(&spxmlDoc);
                if (SUCCEEDED(hr))
                {
                    // Create the new attribute
                    CComPtr<IXMLDOMAttribute> spxmlAttr;
                    hr = spxmlDoc->createAttribute((BSTR)pszName, &spxmlAttr);
                    if (SUCCEEDED(hr))
                    {
                        // Set the value
                        hr = spxmlAttr->put_value(svarVal);
                        if (SUCCEEDED(hr))
                        {
                            hr = spxmlMapAttrs->setNamedItem(spxmlAttr, NULL);
                        }
                    }
                }
            }
        }
    }
    return hr;
}

////////////////////////////////////////////////////////////////////////////////

HRESULT CXMLDocument::Load(__in HINSTANCE hinst, UINT uiResID)
{
    // Load the resource
    char *pszXml;
    size_t cch;
    HRESULT hr = LoadAndLockResource(hinst, uiResID, RT_HTML, (void **)&pszXml, (DWORD *)&cch);
    if (SUCCEEDED(hr))
    {
        // Convert to WCHAR
        CLocalMemPtr<WCHAR> spszXml;
        hr = spszXml.Allocate(cch + 1);
        if (SUCCEEDED(hr))
        {
            hr = MultiByteToWideChar(CP_ACP, 0, pszXml, (int)cch, spszXml, (int)cch) ? S_OK : HRESULT_FROM_WIN32(GetLastError());
            if (SUCCEEDED(hr))
            {
                spszXml[cch] = 0;
                hr = LoadXML(spszXml);
            }
        }
    }
    return hr;
}

HRESULT CXMLDocument::_EnsureXmlDocument()
{
    HRESULT hr = S_OK;
    if (!_spxmlDoc)
    {
        hr = _spxmlDoc.CoCreateInstance(__uuidof(FreeThreadedDOMDocument60), NULL, CLSCTX_INPROC_SERVER);
        if (SUCCEEDED(hr))
        {
            hr = _spxmlDoc->put_async(VARIANT_FALSE);
        }
    }
    return hr;
}

HRESULT CXMLDocument::Load(__in CComVariant &svarSource, BOOL fSynchronous)
{
    // Free up any existing document
    // Create the document if necessary
    HRESULT hr = _EnsureXmlDocument();
    if (SUCCEEDED(hr))
    {
        // Load the document
        VARIANT_BOOL fSucceeded;
        hr = _spxmlDoc->load(svarSource, &fSucceeded);
        if (hr == S_FALSE)
        {
            hr = STG_E_FILENOTFOUND;

#if TODO_DBG
            REPORTXMLERR();
#endif
        }

        if (SUCCEEDED(hr))
        {
            // Establish the root node for this XML document
            hr = _EnsureRootNode();
        }
#if TODO_DBG
        else 
        {
            REPORTXMLERR();
        }
#endif
    }
    return hr;
}

HRESULT CXMLDocument::Load(PCWSTR pszSource, PCWSTR pszPostData, BOOL fSynchronous)
{
    // Create and load with an XML Http Request object
    CComPtr<IXMLHttpRequest> spxmlReq;
    HRESULT hr = CoCreateInstance(__uuidof(XMLHTTPRequest), NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&spxmlReq));
    if (SUCCEEDED(hr))
    {
        // Set up the request object
        BSTR bstrMethod = SysAllocString(L"POST");
        BSTR bstrUrl = SysAllocString(pszSource);
        hr = (bstrMethod && bstrUrl) ? S_OK : E_OUTOFMEMORY;
        if (SUCCEEDED(hr))
        {
            // Open the request
            CComVariant svarAsync;
            CComVariant svarEmpty;

            svarAsync = fSynchronous ? VARIANT_FALSE : VARIANT_TRUE;
            hr = spxmlReq->open(bstrMethod, bstrUrl, svarAsync, svarEmpty, svarEmpty);
            if (SUCCEEDED(hr))
            {
                // Send the request
                CComVariant svarBody = pszPostData;
                hr = spxmlReq->send(svarBody);
                if (SUCCEEDED(hr))
                {
                    // Get the XML back in stream form, and load it into this document
                    CComVariant svarStream;
                    hr = spxmlReq->get_responseStream(&svarStream);
                    if (SUCCEEDED(hr))
                    {
                        // Convert the VARIANT_TYPE to VT_UNKNOWN
                        svarStream.vt = VT_UNKNOWN;
                        hr = Load(svarStream, fSynchronous);
                    }
                }
            }
        }
        SysFreeString(bstrMethod);
        SysFreeString(bstrUrl);
    }
    return hr;
}

HRESULT CXMLDocument::LoadXML(PCWSTR pszSource)
{
    // Create the document if necessary
    HRESULT hr = _EnsureXmlDocument();
    if (SUCCEEDED(hr)) 
    {
        // Load the XML
        VARIANT_BOOL fSucceeded;
        hr = _spxmlDoc->loadXML((BSTR)pszSource, &fSucceeded);
        if (hr == S_FALSE)
        {
            hr = STG_E_FILENOTFOUND;
        }

        if (SUCCEEDED(hr))
        {
            // Establish the root node for this XML document
            hr = _EnsureRootNode();
        }
#if TODO_DBG
        else
        {
            REPORTXMLERR();
        }
#endif
    }
    return hr;
}

HRESULT CXMLDocument::Validate(const CXMLDocument &xmlSchema) const
{
    // Read the target namespace out of the schema and add the schema to the cache
    CComVariant svarTargetNamespace;
    CComPtr<IXMLDOMElement> spxmlElt;
    HRESULT hr = xmlSchema._spxmlDoc->get_documentElement(&spxmlElt);
    if (SUCCEEDED(hr))
    {
        CComPtr<IXMLDOMNamedNodeMap> spxmlMapAttrs;
        hr = spxmlElt->get_attributes(&spxmlMapAttrs);
        if (SUCCEEDED(hr))
        {
            CComPtr<IXMLDOMNode> spxmlNodeAttr;
            hr = spxmlMapAttrs->getNamedItem((BSTR)L"targetNamespace", &spxmlNodeAttr);
            if (SUCCEEDED(hr))
            {
                // Get the value
                hr = spxmlNodeAttr->get_nodeValue(&svarTargetNamespace);
            }
        }
    }

    // Create a schema cache, add the schema and validate
    if (SUCCEEDED(hr))
    {
        CComPtr<IXMLDOMSchemaCollection> spxmlsc;
        hr = spxmlsc.CoCreateInstance(L"Msxml2.XMLSchemaCache", NULL, CLSCTX_INPROC_SERVER);
        if (SUCCEEDED(hr))
        {
            CComVariant svarSchema((IUnknown*)xmlSchema._spxmlDoc);
            hr = spxmlsc->add(svarTargetNamespace.bstrVal, svarSchema);
            if (SUCCEEDED(hr))
            {
                hr = _spxmlDoc->putref_schemas(CComVariant((IDispatch*)spxmlsc));
                if (SUCCEEDED(hr))
                {
                    CComPtr<IXMLDOMParseError> spxmlError;
                    hr = _spxmlDoc->validate(&spxmlError);
                    if (SUCCEEDED(hr))
                    {
                        //XMLERRTRACE(pxmlError);
                        spxmlError->get_errorCode(&hr);
                    }
                }
            }
        }
    }
    return hr;
}

HRESULT CXMLDocument::Clone(__inout CXMLDocument &xmlDocClone) const
{
    ATLASSERT(NULL != _spxmlNode);

    HRESULT hr = _spxmlNode->cloneNode(VARIANT_TRUE, &xmlDocClone._spxmlNode);
    if (SUCCEEDED(hr))
    {
        // Set up the root document for the newly cloned node
        hr = xmlDocClone._spxmlNode->get_ownerDocument((IXMLDOMDocument**)&xmlDocClone._spxmlDoc);
    }
    return hr;
}

HRESULT CXMLDocument::Insert(__in CXMLDocument &xmlDocSrc, PCWSTR pszPatternRoot, PCWSTR pszPatternSrc, PCWSTR pszPatternDst)
{
    // Get the root node of the insert
    CXMLNode xmlNodeRoot;
    HRESULT hr = SelectNode(pszPatternRoot, &xmlNodeRoot);
    if (SUCCEEDED(hr))
    {
        // Set the source to the location to copy from
        CXMLNode xmlNodeSrc;
        hr = xmlDocSrc.SelectNode(pszPatternSrc, &xmlNodeSrc);
        if (SUCCEEDED(hr))
        {
            // Set the destination node
            CXMLNode xmlNodeDst;
            hr = SelectNode(pszPatternDst, &xmlNodeDst);
            if (SUCCEEDED(hr))
            {
                // Perform the insert
                CComVariant svarNode = (IUnknown*)xmlNodeDst;
                hr = ((IXMLDOMNode* )xmlNodeRoot)->insertBefore(xmlDocSrc, svarNode, NULL);
            }
        }
    }
    return hr;
}

HRESULT CXMLDocument::Transform(__in CXMLDocument &xmlXSL, __deref_out BSTR *pbstrTransform) const
{
    ATLASSERT(NULL != _spxmlNode);
    ATLASSERT(NULL != xmlXSL._spxmlNode);
    return _spxmlNode->transformNode(xmlXSL._spxmlNode, pbstrTransform);
}

HRESULT CXMLDocument::Transform(__in CXMLDocument &xmlXSL, __inout CXMLDocument &xmlDocDst) const
{
    HRESULT hr = E_FAIL;
    if (_spxmlNode && xmlXSL._spxmlNode)
    {
        // To transform, get the IDispatch of the destination document and use it as the
        // target of the transform
        hr = xmlDocDst._EnsureXmlDocument();
        if (SUCCEEDED(hr))
        {
            CComPtr<IDispatch> spdisp;
            hr = xmlDocDst._spxmlDoc->QueryInterface(IID_PPV_ARGS(&spdisp));
            if (SUCCEEDED(hr))
            {
                CComVariant svarThis = spdisp;
                hr = _spxmlNode->transformNodeToObject(xmlXSL._spxmlNode, svarThis);
                if (SUCCEEDED(hr))
                {
                    // Reset to the beginning
                    hr = xmlDocDst._EnsureRootNode();
                }
            }
        }
    }
    return hr;
}

HRESULT CXMLDocument::_EnsureRootNode()
{
    _spxmlNode = NULL;
    CComPtr<IXMLDOMElement> spxmlElt;
    HRESULT hr = _spxmlDoc->get_documentElement(&spxmlElt);
    if (SUCCEEDED(hr))
    {
        hr = spxmlElt->QueryInterface(IID_PPV_ARGS(&_spxmlNode));
    }
    return hr;
}

////////////////////////////////////////////////////////////////////////////////

HRESULT CXMLNodeList::GetNodeCount(__out long *pcNodes) const
{
    HRESULT hr = E_FAIL;
    if (_spxmlNodeList)
    {
        hr = E_INVALIDARG;
        if (pcNodes)
        {
            *pcNodes = 0;
            hr = _spxmlNodeList->get_length(pcNodes);
        }
    }
    return hr;
}
